﻿namespace Sai.Test.Introspection
{
    using System.Linq;
    using Sai.Core;
    using Sai.Core.Introspection;
    using Sai.Test.TestDataBuilders;
    using Sai.Test.XUnit;
    using Xunit;

    public class MethodDeclarationCollectionFacts
    {
        public class TheDistinctMethodsProperty
        {
            [Fact, InMemoryTest]
            public void Returns_Single_Method_Name_For_No_Overload()
            {
                string methodName = "Example";
                var returnType = new TypeName(new FullyQualifiedName("string"));

                var methods = new MethodDeclarationCollection();

                methods.Add(MethodDeclarationBuilder.Method()
                                .Named(methodName)
                                .WithReturnType(returnType)
                                .Build());

                Assert.Equal(1, methods.DistinctMethods.Count);
                Assert.Equal("Example", methods.DistinctMethods.First());
            }

            [Fact, InMemoryTest]
            public void Returns_Two_Method_Names_For_No_Overloads()
            {
                string methodName = "Example";
                string methodName2 = "Example2";
                var returnType = new TypeName(new FullyQualifiedName("string"));

                var methods = new MethodDeclarationCollection();

                methods.Add(MethodDeclarationBuilder.Method()
                                .Named(methodName)
                                .WithReturnType(returnType)
                                .Build());
                methods.Add(MethodDeclarationBuilder.Method()
                                .Named(methodName2)
                                .WithReturnType(returnType)
                                .Build());

                Assert.Equal(2, methods.DistinctMethods.Count);
                Assert.Equal("Example", methods.DistinctMethods[0]);
                Assert.Equal("Example2", methods.DistinctMethods[1]);
            }

            [Fact, InMemoryTest]
            public void Returns_Single_Method_Name_For_Two_Overloads()
            {
                string methodName = "Example";
                var returnType = new TypeName(new FullyQualifiedName("string"));

                var methods = new MethodDeclarationCollection();

                methods.Add(MethodDeclarationBuilder.Method()
                                .Named(methodName)
                                .WithReturnType(returnType)
                                .Build());
                methods.Add(MethodDeclarationBuilder.Method()
                                .Named(methodName)
                                .WithReturnType(returnType)
                                .WithParameter("System.String")
                                .Build());

                Assert.Equal(1, methods.DistinctMethods.Count);
                Assert.Equal("Example", methods.DistinctMethods.First());
            }
        }

        public class TheOverloadsForMethod
        {
            [Fact, InMemoryTest]
            public void Returns_Single_Method_For_No_Overloads()
            {
                string methodName = "Example";
                var returnType = new TypeName(new FullyQualifiedName("string"));

                var methods = new MethodDeclarationCollection();

                methods.Add(MethodDeclarationBuilder.Method()
                                .Named(methodName)
                                .WithReturnType(returnType)
                                .Build());

                var overloads = methods.OverloadsFor(methods.DistinctMethods[0]);

                Assert.Equal(1, overloads.Count);
            }

            [Fact, InMemoryTest]
            public void Returns_Two_Methods_For_Two_Overloads()
            {
                string methodName = "Example";
                var returnType = new TypeName(new FullyQualifiedName("string"));

                var methods = new MethodDeclarationCollection();

                methods.Add(MethodDeclarationBuilder.Method()
                                .Named(methodName)
                                .WithReturnType(returnType)
                                .Build());
                methods.Add(MethodDeclarationBuilder.Method()
                                .Named(methodName)
                                .WithReturnType(returnType)
                                .WithParameter("System.String")
                                .Build());

                var overloads = methods.OverloadsFor(methods.DistinctMethods.First());

                Assert.Equal(2, overloads.Count);

                foreach (var method in overloads)
                {
                    Assert.True(method.ToString().Contains("Example"));
                }
            }
        }

        public class TheNonOverloadsForMethod
        {
            [Fact, InMemoryTest]
            public void Returns_Single_Method_For_No_Overloads()
            {
                string methodName = "Example";
                var returnType = new TypeName(new FullyQualifiedName("string"));

                var methods = new MethodDeclarationCollection();

                methods.Add(MethodDeclarationBuilder.Method()
                                .Named(methodName)
                                .WithReturnType(returnType)
                                .Build());

                Assert.Equal(1, methods.NonOverloadedMethods.Count);
            }

            [Fact, InMemoryTest]
            public void Returns_Empty_Collection_For_Two_Overloads()
            {
                string methodName = "Example";
                var returnType = new TypeName(new FullyQualifiedName("string"));

                var methods = new MethodDeclarationCollection();

                methods.Add(MethodDeclarationBuilder.Method()
                                .Named(methodName)
                                .WithReturnType(returnType)
                                .Build());
                methods.Add(MethodDeclarationBuilder.Method()
                                .Named(methodName)
                                .WithReturnType(returnType)
                                .WithParameter("System.String")
                                .Build());

                Assert.Equal(0, methods.NonOverloadedMethods.Count);
            }

            [Fact, InMemoryTest]
            public void Returns_One_Method_For_Two_Overloads_One_Plain()
            {
                string methodName = "Example";
                var returnType = new TypeName(new FullyQualifiedName("string"));

                var methods = new MethodDeclarationCollection();

                methods.Add(MethodDeclarationBuilder.Method()
                                .Named(methodName)
                                .WithReturnType(returnType)
                                .Build());
                methods.Add(MethodDeclarationBuilder.Method()
                                .Named(methodName)
                                .WithReturnType(returnType)
                                .WithParameter("System.String")
                                .Build());

                methods.Add(MethodDeclarationBuilder.Method()
                                .Named("NewMethod")
                                .WithReturnType(returnType)
                                .WithParameter("System.String")
                                .Build());

                Assert.Equal(1, methods.NonOverloadedMethods.Count);
            }
        }
    }
}
